package lib

import (
	"fmt"
	"fruit/actor/iactor"
	"fruit/common"
	"fruit/tools/proto/proto_out"
	"fruit/tools/proto/proto_out/base"
	"os"
	"reflect"
	"strconv"
	"time"

	"google.golang.org/protobuf/proto"
)

func RegisterActor(actor_type string, iact iactor.IActor, data []byte) interface{} {
	uid := pid + "." + strconv.FormatInt(Index("actor"), 10)

	ref_val := reflect.ValueOf(iact).Elem()
	ref_act := ref_val.FieldByName("Actor")
	if ref_act.IsNil() {
		act := iactor.NewActor(uid, actor_type)
		ref_act.Set(reflect.ValueOf(act))
	}

	map_actor.Store(uid, iact)
	resp := iact.InitIActor(data)
	RunGoroutine(iact, "deal_msg", func(iact iactor.IActor, args ...interface{}) {
		for {
			args, life := iactor.Pop(iact)
			if !life {
				break
			}
			from, event_id, func_name, msg := args[0].(string), args[1].(uint64), args[2].(uint64), args[3].([]byte)
			resp := proto_out.DealMsg(iact, func_name, msg)
			if event_id > 0 {
				// parse := common.Adapt(from)
				msg := &base.WakeUpReq{
					EventId: event_id,
					From:    iactor.Uid(iact),
					Data: func() []byte {
						data, _ := proto.Marshal(resp)
						return data
					}(),
				}
				push(iact, from, proto_out.ProtoFuncEnum_WakeUp, 0, msg)
			}
		}
	})

	// lib_rds.sadd(fmt.Sprintf("actor:%s", iactor.Type(iact)), iactor.Uid(iact))
	fmt.Printf("%s 注册成功 uid: %s name: %s\n", actor_type, uid, actor_type)
	return resp
}

func UnregisterActor(uid string) {
	val, loaded := map_actor.LoadAndDelete(uid)
	if !loaded {
		panic(fmt.Sprintf("未找到actor %s\n", uid))
		// fmt.Printf("未找到actor %s\n", uid)
		// return
	}
	iact := val.(iactor.IActor)

	fmt.Printf("%s 销毁成功 uid: %s name: %s\n", uid, iactor.Uid(iact), iactor.Name(iact))
	iactor.DestoryActor(iact)
}

func create_service(iact iactor.IActor, args ...interface{}) {
	for i := 1; i < len(os.Args); i++ {
		map_service.Store(os.Args[i], 1)
		msg := Create(os.Args[i], &base.CreateReq{
			Type: os.Args[i],
		})
		if msg != nil {
			// map_service.Store(os.Args[i], msg.Uid)
			// todo 将del放入脚本
			lib_rds.del(fmt.Sprintf("service:%s", os.Args[i]))

			lib_rds.sadd(fmt.Sprintf("service:%s", os.Args[i]), msg.Uid)
		}
	}
}

func call(to string, func_name uint64, msg proto.Message, out proto.Message) bool {
	iact := get_actor()
	if iact == nil {
		panic("act not found")
		return false
	}
	from := iactor.Uid(iact)
	if from == to {
		//	actor内部，直接进行逻辑操作
		var req []byte
		if msg != nil {
			req, _ = proto.Marshal(msg)
		}
		resp_inf := proto_out.DealMsg(iact, func_name, req)
		if out != nil {
			proto.Merge(out, resp_inf)
		}
	} else {
		//	actor外部，转发
		ch := make(chan []byte)
		event_id := sli_event.Add(ch)
		push(iact, to, func_name, event_id, msg)
		select {
		case str := <-ch:
			if out != nil {
				err := proto.Unmarshal(str, out)
				if err != nil {
					panic("error")
				}
			}
		case <-time.After(time.Second * 15):
			fmt.Println("call 超时", func_name)
		}
		close(ch)
	}
	return true
}

func push(from_act iactor.IActor, to string, func_name uint64, event_id uint64, msg proto.Message) {
	var str_msg []byte
	var err error
	if msg != nil {
		str_msg, err = proto.Marshal(msg)
		if err != nil {
			fmt.Println("error proto Marshal")
			if event_id > 0 {
				//	todo check 是否报错
				Push(iactor.Uid(from_act), proto_out.ProtoFuncEnum_WakeUp, nil)
			}
			return
		}
	}

	from := iactor.Uid(from_act)
	if from == to {
		//	actor内部，直接进行逻辑操作
		proto_out.DealMsg(from_act, func_name, str_msg)
		return
	}

	parse := common.Adapt(to)
	is_my_service := parse.Service == get_pid()
	deal_to := common.IfGet(is_my_service, "同步", "异步").(string)
	deal_type := common.IfGet(event_id != 0, "call", "push").(string)
	fmt.Printf("%s %s %s %s func_name:%d event_id:%d\n", from, deal_to, deal_type, to, func_name, event_id)
	if parse.Service == get_pid() {
		// fmt.Println("local server ", from, to, event_id, func_name)
		deal(from, to, func_name, event_id, true, str_msg)
	} else {
		// fmt.Println("remote server ", from, to, event_id, func_name)
		rds_msg := &base.Remote{
			FuncName: func_name,
			From:     from,
			EventId:  event_id,
			Data:     str_msg,
		}
		str_msg, err = proto.Marshal(rds_msg)
		if err != nil {
			fmt.Println("error proto Marshal")
			return
		}
		lib_rds.publish(to, str_msg)
	}
}

func deal(from string, to string, func_name uint64, event_id uint64, service_inside bool, msg []byte) {
	// fmt.Println("33333333", from, to, func_name)
	inf, loaded := map_actor.Load(to)
	if loaded {
		iactor.Push(inf.(iactor.IActor), from, event_id, func_name, msg)
		// proto_out.DealMsg(inf.(iactor.IActor), func_name, msg)
	}
}

func Index(idx_type string) int64 {
	return lib_rds.Incr("Index:" + idx_type)
}

func RunGoroutine(act iactor.IActor, asyn_func_name string, asyn_func AsynActorFunc, args ...interface{}) {
	go func(inf iactor.IActor) {
		rouId := common.GetGorId()
		_, loaded := map_rou.LoadOrStore(rouId, inf)
		if loaded {
			panic("error")
		}
		fmt.Printf("%s-%s 建立协程 %d 执行 %s\n", *iactor.Get(inf, iactor.Actor_Name), *iactor.Get(inf, iactor.Actor_Uid), rouId, asyn_func_name)
		asyn_func(inf, args...)
		//	处理完毕进行移除
		_, loaded = map_rou.LoadAndDelete(rouId)
		str_succ := common.IfGet(loaded, "成功", "失败").(string)
		fmt.Printf("%s-%s 移除协程 %d %s 函数名 %s\n", *iactor.Get(inf, iactor.Actor_Name), *iactor.Get(inf, iactor.Actor_Uid), rouId, str_succ, asyn_func_name)
	}(act)
}

// func is_in_my_services(name string) bool {
// 	_, succ := map_service.Load(name)
// 	return succ
// }

// func get_service_uid(name string) string {
// 	uid, succ := map_service.Load(name)
// 	if succ {
// 		return (uid.(string))
// 	}
// 	return ""
// }

func get_actor() iactor.IActor {
	rouid := common.GetGorId()
	a, loaded := map_rou.Load(rouid)
	if loaded {
		return a.(iactor.IActor)
	}
	return nil
}

func get_pid() string {
	return iactor.Uid(proto_actor.(iactor.IActor))
}

func get_default_uid() string {
	return iactor.Uid(proto_actor.(iactor.IActor))
}

func GetServiceId(service string) string {
	parse := common.Adapt(service)
	return lib_rds.srandmember(fmt.Sprintf("service:%s", parse.Service))
}
